import PhuketApi from '@/api/process/phuket'
import Constant from '../Constant'
import Common from '../Common'
import Utils from '../Utils'

// 节点参数面板缓存信息
let shapePanels = {}

/**
 * 清空节点参数面板缓存信息
 */
export function emptyShapePanels() {
  shapePanels = {}
}

/**
 * 获取Shape面板配置
 *
 * @param {*} self
 * @param {*} modeler
 * @param {*} shape
 */
export function getShapePanel(self, modeler, shape) {
  let tabs = shapePanels[shape.id]
  if (tabs) {
    return tabs
  }
  tabs = initTabs(modeler, shape)
  const stage = shape.businessObject.$attrs.stage
  const stageConfig = Common.getConfig(stage)
  // console.log('stageConfig', stageConfig)
  const stageBeDepends = getStageBeDepends(stageConfig)
  // console.log('stageBeDepends', stageBeDepends)
  for (const group in stageConfig) {
    const items = stageConfig[group]
    let rule = []
    if (group === Constant.TAB_NAME_GENERAL) {
      rule = tabs[0].rule
    }
    for (const i in items) {
      const item = items[i]
      const type = toUpperCase(item.type)
      const model = toUpperCase(item.model)
      const mode = toUpperCase(item.mode)
      const beDepends = stageBeDepends[item.field]
      let r = {}
      if (type === 'STRING' || type === 'PASSWORD' || type === 'CHARACTER' ||
      (type === 'TEXT' && mode === 'PLAIN_TEXT')) {
        r = inputRule(self, modeler, shape, tabs, stage, item, beDepends)
      } else if (type === 'TEXT' && mode !== 'PLAIN_TEXT') {
        r = pktCodeMirrorRule(modeler, shape, tabs, stage, item)
      } else if (type === 'BOOLEAN') {
        r = switchRule(self, modeler, shape, tabs, stage, item, beDepends)
      } else if (type === 'NUMBER') {
        r = inputNumberRule(self, modeler, shape, tabs, stage, item, beDepends)
      } else if (type === 'MODEL' &&
      (model === 'VALUE_CHOOSER' || model === 'MULTI_VALUE_CHOOSER' || model === 'MULTI_VALUE_INPUT')) {
        r = selectRule(self, modeler, shape, tabs, stage, item, beDepends)
      } else if (type === 'LIST') {
        r = pktMultiInput(modeler, shape, tabs, item, beDepends)
      } else if (type === 'MAP') {
        r = pktMultiPairInput(modeler, shape, item)
      } else if (type === 'EVENT') {
        r = pktEventButtonRule(self, tabs, stage, item)
      } else if (type === 'URL') {
        r = pktEmbedUrl(item)
      }
      rule.push(r)
    }
    if (group !== Constant.TAB_NAME_GENERAL) {
      tabs.push({
        name: group,
        label: group,
        disabled: false,
        form: {},
        rule: rule
      })
    }
  }
  shapePanels[shape.id] = tabs
  return tabs
}

/**
 * 获取被依赖信息集合
 *
 * @param {*} stageConfig
 */
function getStageBeDepends(stageConfig) {
  const stageDepends = getStageDepends(stageConfig)
  const stageBeDepends = {}
  for (const target in stageDepends) {
    const depends = stageDepends[target]
    for (const key in depends) {
      let list = stageBeDepends[key]
      if (!list) {
        list = []
      }
      list.push({ 'depends': depends, 'target': target })
      stageBeDepends[key] = list
    }
  }
  // console.log(stageBeDepends)
  return stageBeDepends
}

/**
 * 获取依赖信息集合
 *
 * @param {*} stageConfig
 */
function getStageDepends(stageConfig) {
  const stageDepends = {}
  for (const group in stageConfig) {
    const items = stageConfig[group]
    items.forEach(item => {
      if (item.depends) {
        const depends = {}
        item.depends.forEach(depend => {
          depends[depend.configName] = depend.triggeredByValues
        })
        stageDepends[item.field] = depends
      }
    })
  }
  // console.log(stageDepends)
  return stageDepends
}

/**
 * 初始化标签栏信息
 *
 * @param {*} modeler
 * @param {*} shape
 */
function initTabs(modeler, shape) {
  return [{
    name: Constant.TAB_NAME_GENERAL,
    label: '常规',
    disabled: false,
    form: {},
    rule: [
      {
        type: 'input',
        field: 'id',
        title: '节点ID',
        value: shape.businessObject.id,
        props: {
          disabled: true
        }
      },
      {
        type: 'input',
        field: 'name',
        title: '节点名称',
        value: shape.businessObject.name,
        props: {
          type: 'text',
          maxlength: 32
        },
        validate: [
          {
            required: true,
            message: '请输入节点名称',
            trigger: 'blur'
          }
        ],
        on: {
          change: function(val) {
            modeler.get('modeling').updateLabel(shape, val)
          }
        }
      }
    ]
  }]
}

/**
 * 生成事件按钮Rule
 *
 * @param {Object} self
 * @param {Array} tabs
 * @param {String} stageName
 * @param {Object} item
 */
function pktEventButtonRule(self, tabs, stageName, item) {
  return {
    type: 'pkt-event-button',
    hidden: item.hidden,
    name: item.field,
    props: {
      text: item.label,
      type: 'primary',
      justify: 'end',
      disabled: false,
      onClick: pktEventButtonClickEvent(self, tabs, stageName, item.field, item.parameters)
    }
  }
}

/**
 * 事件按钮的点击事件
 *
 * @param {Object} self
 * @param {Array} tabs
 * @param {String} stageName
 * @param {String} fieldName
 * @param {Array} parameters
 */
function pktEventButtonClickEvent(self, tabs, stageName, fieldName, parameters) {
  return function() {
    const args = {}
    if (parameters) {
      tabs.forEach(t => {
        t.rule.forEach(r => {
          if (parameters.indexOf(r.field) > -1) { // form-create自带表单
            args[r.field] = r.value
          } else if (parameters.indexOf(r.name) > -1) { // 自定义表单
            args[r.name] = getPktRuleValue(r)
          }
        })
      })
    }
    eventButtonOnClick(self, stageName, fieldName, args)
  }
}

/**
 * 获取自定义表单规则的值
 *
 * @param {Object} rule
 */
function getPktRuleValue(rule) {
  const type = rule.type
  if (type === 'pkt-multi-input') {
    return rule.props.defaults
  } else if (type === 'pkt-code-mirror') {
    return rule.props.code
  }
  return null
}

/**
 * 自定义的事件按钮点击时触发
 *
 * @param {*} self
 * @param {*} stageName
 * @param {*} methodName
 * @param {*} args
 */
function eventButtonOnClick(self, stageName, methodName, args) {
  PhuketApi.event(stageName, methodName, args).then(obj => {
    if (obj.status) {
      Utils.notify(self, '成功', obj.message, 'success')
    } else {
      Utils.notify(self, '失败', obj.message, 'error')
    }
  }).catch(() => {})
}

/**
 * 动态选择器的获得焦点事件
 *
 * @param {Object} self
 * @param {Array} tabs
 * @param {Object} rule
 * @param {String} stageName
 * @param {String} methodName
 * @param {Array} parameters
 */
function selectRuleFocusEvent(self, tabs, rule, stageName, methodName, parameters) {
  return function() {
    const args = {}
    if (parameters) {
      tabs.forEach(t => {
        t.rule.forEach(r => {
          if (parameters.indexOf(r.field) > -1) { // form-create自带表单
            args[r.field] = r.value
          } else if (parameters.indexOf(r.name) > -1) { // 自定义表单
            args[r.name] = getPktRuleValue(r)
          }
        })
      })
    }
    selectRuleOnFocus(self, rule, stageName, methodName, args)
  }
}

/**
 * 选择栏获得焦点时触发
 *
 * @param {Object} self
 * @param {Object} rule
 * @param {String} stageName
 * @param {String} methodName
 * @param {Object} args
 */
function selectRuleOnFocus(self, rule, stageName, methodName, args) {
  rule.options = []
  rule.value = null
  rule.props.loading = true
  PhuketApi.event(stageName, methodName, args).then(obj => {
    if (obj.status) {
      rule.options = obj.data
    } else {
      Utils.notify(self, '失败', obj.message, 'error')
    }
    rule.props.loading = false
  }).catch(() => {})
}

/**
 * 根据单选下拉框选择的内容，动态生成代码编辑器模板
 * @param {*} self
 * @param {*} tabs
 * @param {*} stageName
 * @param {*} rule
 * @param {*} item
 */
function selectTemplate(self, tabs, stageName, rule, item) {
  const args = {}
  if (item.parameters.indexOf(rule.field) > -1) {
    args[rule.field] = rule.value
    PhuketApi.event(stageName, item.methodName, args).then(obj => {
      if (obj.status) {
        tabs.forEach(tab => {
          tab.rule.forEach(rule1 => {
            // tabs中定位联动的代码编辑器组件的rule
            if (rule1.type === 'pkt-code-mirror' && rule1.name === item.targetField) {
              rule1.props.code = obj.message
            }
          })
        })
      } else {
        Utils.notify(self, '失败', obj.message, 'error')
      }
    }).catch(() => {
    })
  }
}

/**
 * Rule设置多值和对应的事件处理
 *
 * @param {*} modeler
 * @param {*} shape
 * @param {*} tabs
 * @param {*} rule
 * @param {*} beDepends
 */
function ruleSetMultiValueAndEvent(modeler, shape, tabs, rule, beDepends) {
  const value = Utils.getFieldValue(shape, rule.field)
  if (value) { // xml中有值则赋值给对应的rule
    rule.value = JSON.parse(value)
  } else if (rule.value) { // 否则如果rule有默认值则写入xml
    Utils.setFieldValue(modeler, shape, rule.field, JSON.stringify(rule.value))
  }
  if (!rule.on) {
    rule.on = {}
  }
  rule.on['change'] = function(vals) {
    Utils.setFieldValue(modeler, shape, rule.field, JSON.stringify(vals))
    dependencies(tabs, beDepends)
  }
  return rule
}

/**
 * Rule设置单值和对应的事件处理
 *
 * @param {Object} modeler
 * @param {Object} shape
 * @param {Object} rule
 * @param {Array} beDepends
 */
function ruleSetValueAndEvent(self, modeler, shape, tabs, stageName, rule, item, beDepends) {
  const value = Utils.getFieldValue(shape, rule.field)
  if (value) { // xml中有值则赋值给对应的rule
    rule.value = value
  } else if (rule.value) { // 否则如果rule有默认值则写入xml
    Utils.setFieldValue(modeler, shape, rule.field, rule.value)
  }
  if (!rule.on) {
    rule.on = {}
  }
  rule.on['change'] = function(val) {
    Utils.setFieldValue(modeler, shape, rule.field, val)
    dependencies(tabs, beDepends)
    // 找出具有触发功能的单选下拉框组件
    if (toUpperCase(item.model) === 'VALUE_CHOOSER' && item.targetField) {
      // 修改被触发组件的值
      selectTemplate(self, tabs, stageName, rule, item)
    }
  }
  return rule
}

/**
 * 依赖关系处理
 *
 * @param {Array} tabs
 * @param {Array} beDepends
 */
function dependencies(tabs, beDepends) {
  if (beDepends) {
    beDepends.forEach(beDepend => {
      const depends = beDepend.depends
      const target = beDepend.target
      let count = 0
      tabs.forEach(t => {
        t.rule.forEach(r => {
          let triggeredByValues
          let value
          if (depends[r.field]) { // form-create自带表单
            triggeredByValues = depends[r.field]
            value = r.value
          } else if (depends[r.name]) { // 自定义表单
            triggeredByValues = depends[r.name]
            value = getPktRuleValue(r)
          }
          if (value && triggeredByValues) {
            if ((Utils.isString(value) && triggeredByValues[0] === value) ||
              (Utils.isNumber(value) && triggeredByValues[0] === value.toString()) ||
              (Utils.isArray(value) && triggeredByValues.sort().toString() === value.sort().toString())) {
              count++
            }
          }
        })
      })
      tabs.forEach(t => {
        t.rule.forEach(r => {
          if (target === r.field || target === r.name) {
            if (count === Object.keys(depends).length) {
              // r.props.disabled = false
              r.hidden = false
            } else {
              // r.props.disabled = true
              r.hidden = true
            }
          }
        })
      })
    })
  }
}

/**
 * 生成输入框Rule
 *
 * @param {Object} modeler
 * @param {Object} shape
 * @param {Object} item
 * @param {Array} beDepends
 */
function inputRule(self, modeler, shape, tabs, stage, item, beDepends) {
  const rule = {
    type: 'input',
    hidden: item.hidden,
    field: item.field,
    title: item.label,
    value: item.defaultValue,
    info: item.description,
    props: {
      type: 'text',
      disabled: item.disabled,
      readonly: item.readonly,
      minlength: item.minLength,
      maxlength: item.maxLength
    },
    validate: [
      {
        required: item.required,
        message: '请输入内容',
        trigger: 'blur'
      }
    ]
  }
  const type = toUpperCase(item.type)
  if (type === 'TEXT') {
    rule.props.type = 'textarea'
    rule.props.rows = item.lines
  } else if (type === 'PASSWORD') {
    rule.props.type = 'password'
  }
  return ruleSetValueAndEvent(self, modeler, shape, tabs, stage, rule, item, beDepends)
}

/**
 * 生成代码编辑器Rule
 *
 * @param {Object} modeler
 * @param {Object} shape
 * @param {Object} item
 */
function pktCodeMirrorRule(modeler, shape, tabs, stageName, item) {
  const rule = {
    type: 'pkt-code-mirror',
    hidden: item.hidden,
    name: item.field,
    title: item.label,
    info: item.description,
    props: {
      disabled: item.disabled,
      code: item.defaultValue,
      mode: item.mode
    }
  }
  const value = Utils.getFieldValue(shape, rule.name)
  if (value) { // xml中有值则赋值给对应的rule
    rule.props.code = value
  } else if (rule.props.code) { // 否则如果rule有默认值则写入xml
    Utils.setFieldValue(modeler, shape, rule.name, rule.props.code)
  }
  rule.props.onChange = function(val) {
    console.log('pkt-code-mirror change', val)
    rule.props.code = val
    Utils.setFieldValue(modeler, shape, rule.name, val)
  }
  return rule
}

/**
 * 生成开关Rule
 *
 * @param {Object} modeler
 * @param {Object} shape
 * @param {Array} tabs
 * @param {Object} item
 * @param {Array} beDepends
 */
function switchRule(self, modeler, shape, tabs, stage, item, beDepends) {
  const rule = {
    type: 'switch',
    hidden: item.hidden,
    field: item.field,
    title: item.label,
    info: item.description,
    value: item.defaultValue,
    props: {
      disabled: item.disabled,
      inactiveValue: 'false', // 关闭
      activeValue: 'true' // 开启
    }
  }
  return ruleSetValueAndEvent(self, modeler, shape, tabs, stage, rule, item, beDepends)
}

/**
 * 生成数值输入框Rule
 *
 * @param {*} modeler
 * @param {*} shape
 * @param {*} item
 */
function inputNumberRule(self, modeler, shape, tabs, stage, item, beDepends) {
  const rule = {
    type: 'inputNumber',
    hidden: item.hidden,
    field: item.field,
    title: item.label,
    info: item.description,
    value: item.defaultValue,
    props: {
      disabled: item.disabled,
      min: item.min,
      max: item.max,
      step: item.step,
      precision: item.precision
    }
  }
  return ruleSetValueAndEvent(self, modeler, shape, tabs, stage, rule, item, beDepends)
}

/**
 * 生成内嵌超链接页面Rule
 *
 * @param {*} item
 */
function pktEmbedUrl(item) {
  const rule = {
    type: 'pkt-embed-url',
    hidden: item.hidden,
    name: item.field,
    props: {
      justify: 'center',
      label: item.label,
      href: item.defaultValue
    }
  }
  return rule
}

/**
 * 生成多键值对动态输入框Rule
 *
 * @param {*} modeler
 * @param {*} shape
 * @param {*} item
 */
function pktMultiPairInput(modeler, shape, item) {
  const rule = {
    type: 'pkt-multi-pair-input', // 自定义的多键值对输入框组件
    hidden: item.hidden,
    name: item.field, // 自定义组件需要使用name而不是field
    title: item.label,
    info: item.description,
    props: {
      disabled: item.disabled,
      min: item.minNum,
      max: item.maxNum,
      keyPlaceholder: '请输入键',
      valuePlaceholder: '请输入值',
      defaults: getKVDefaultValues(item.defaultValue, item.disabled)
    }
  }
  const value = Utils.getFieldValue(shape, rule.name)
  if (value) { // xml中有值则赋值给对应的rule
    rule.props.defaults = getKVDefaultValues(value, item.disabled)
  } else if (rule.props.defaults) { // 否则如果rule有默认值则写入xml
    setMapFieldValue(modeler, shape, rule.name, rule.props.defaults)
  }
  rule.props.onKeyChange = function(index, key) {
    // console.log('onKeyChange', index, key)
    rule.props.defaults[index].key = key
    setMapFieldValue(modeler, shape, rule.name, rule.props.defaults)
  }
  rule.props.onValueChange = function(index, value) {
    // console.log('onValueChange', index, value)
    rule.props.defaults[index].value = value
    setMapFieldValue(modeler, shape, rule.name, rule.props.defaults)
  }
  rule.on = {
    add: function(index, item) {
      // console.log("add", index);
      if (!item) {
        rule.props.defaults.unshift({ key: '', value: '' }) // 添加到开头
        setMapFieldValue(modeler, shape, rule.name, rule.props.defaults)
      }
    },
    remove: function(index) {
      // console.log("remove", index);
      rule.props.defaults.splice(index, 1) // 删除元素
      setMapFieldValue(modeler, shape, rule.name, rule.props.defaults)
    }
  }
  return rule
}

function getKVDefaultValues(defaultValue, disabled) {
  if (!defaultValue) {
    return []
  }
  const values = JSON.parse(defaultValue)
  let i = 0
  const defaults = []
  for (const key in values) {
    defaults[i] = { key: key, value: values[key], disabled: disabled }
    i++
  }
  return defaults
}

/**
 * 将键值对集合数据写入XML
 *
 * @param {Object} modeler
 * @param {Object} shape
 * @param {String} fieldName
 * @param {Array} defaults
 */
function setMapFieldValue(modeler, shape, fieldName, defaults) {
  const values = {}
  defaults.forEach(item => {
    values[item.key] = item.value
  })
  Utils.setFieldValue(modeler, shape, fieldName, JSON.stringify(values))
}

/**
 * 生成多值动态输入框Rule
 *
 * @param {*} modeler
 * @param {*} shape
 * @param {*} tabs
 * @param {*} item
 * @param {*} beDepends
 */
function pktMultiInput(modeler, shape, tabs, item, beDepends) {
  const rule = {
    type: 'pkt-multi-input', // 自定义的多值输入框组件
    hidden: item.hidden,
    name: item.field, // 自定义组件需要使用name而不是field
    title: item.label,
    info: item.description,
    props: {
      disabled: item.disabled,
      min: item.field === Constant.NAME_CONDITIONS ? 0 : item.minNum,
      max: item.field === Constant.NAME_CONDITIONS ? 5 : item.maxNum,
      placeholder: item.field === Constant.NAME_CONDITIONS ? '请输入条件' : '请输入',
      defaults: getVDefaultValues(item.defaultValue, item.field, item.disabled)
    }
  }
  const value = Utils.getFieldValue(shape, rule.name)
  if (value) { // xml中有值则赋值给对应的rule
    rule.props.defaults = getVDefaultValues(value, rule.name, item.disabled)
  } else if (rule.props.defaults) { // 否则如果rule有默认值则写入xml
    setListFieldValue(modeler, shape, rule.name, rule.props.defaults)
  }
  rule.props.onChange = function(index, value) {
    // console.log('onChange', index, value)
    rule.props.defaults[index].value = value
    setListFieldValue(modeler, shape, rule.name, rule.props.defaults)
    dependencies(tabs, beDepends)
  }
  rule.on = {
    add: function(index, item) {
      // console.log("add", index);
      if (!item) {
        rule.props.defaults.unshift({ value: '', disabled: false }) // 添加到开头
        setListFieldValue(modeler, shape, rule.name, rule.props.defaults)
      }
      // 如果字段名为conditions则说明是分流器的条件
      if (rule.name === Constant.NAME_CONDITIONS) {
        // 更新connection参数已达到触发重新渲染connection的效果
        shape.outgoing.forEach(connection => {
          Utils.updateElement(modeler, connection, {
            '_refresh': !connection.businessObject.$attrs._refresh
          })
        })
      }
    },
    remove: function(index) {
      // console.log("remove", index);
      rule.props.defaults.splice(index, 1) // 删除元素
      setListFieldValue(modeler, shape, rule.name, rule.props.defaults)
      // 如果字段名为conditions则说明是分流器的条件
      if (rule.name === Constant.NAME_CONDITIONS) {
        // 先批量删除满足条件的Connection
        shape.outgoing.forEach(connection => {
          const conditionNum = connection.businessObject.$attrs.conditionNum
          if (conditionNum && conditionNum === index + 1) {
            Utils.removeElement(modeler, connection)
          }
        })
        // 然后批量更新剩下的Connection参数
        shape.outgoing.forEach(connection => {
          const conditionNum = connection.businessObject.$attrs.conditionNum
          const num = parseInt(conditionNum)
          const properties = { '_refresh': !connection.businessObject.$attrs._refresh }
          // 大于删除条件序号的需要将连线的条件序号-1
          if (num > index + 1) {
            properties['conditionNum'] = num - 1
          }
          // 更新connection参数已达到触发重新渲染connection的效果
          Utils.updateElement(modeler, connection, properties)
        })
      }
    }
  }
  return rule
}

function getVDefaultValues(defaultValue, fieldName, disabled) {
  if (!defaultValue) {
    return []
  }
  const defaults = []
  const list = JSON.parse(defaultValue)
  for (let i = 0; i < list.length; i++) {
    const val = list[i]
    const dis = disabled || (fieldName === Constant.NAME_CONDITIONS && val === Constant.CONDITION_DEFAULT)
    defaults[i] = { value: val, disabled: dis }
  }
  return defaults
}

/**
 * 将值集合数据写入XML
 *
 * @param {Obejct} modeler
 * @param {Obejct} shape
 * @param {String} fieldName
 * @param {Array} defaults
 */
function setListFieldValue(modeler, shape, fieldName, defaults) {
  const values = []
  defaults.forEach(item => {
    values.push(item.value)
  })
  Utils.setFieldValue(modeler, shape, fieldName, JSON.stringify(values))
}

/**
 * 生成下拉框Rule
 *
 * @param {Object} self
 * @param {Object} modeler
 * @param {Object} shape
 * @param {Array} tabs
 * @param {String} stageName
 * @param {Object} item
 * @param {Array} beDepends
 */
function selectRule(self, modeler, shape, tabs, stageName, item, beDepends) {
  const rule = {
    type: 'select',
    hidden: item.hidden,
    field: item.field,
    title: item.label,
    info: item.description,
    options: item.options,
    props: {
      disabled: item.disabled
    }
  }
  const type = toUpperCase(item.type)
  const model = toUpperCase(item.model)
  if (type === 'MODEL' && model === 'VALUE_CHOOSER') { // 单选
    rule.value = item.defaultValue
    rule.props.multiple = false
    if (item.methodName && !item.targetField) {
      rule.on = {
        focus: selectRuleFocusEvent(self, tabs, rule, stageName, item.methodName, item.parameters)
      }
    }
    return ruleSetValueAndEvent(self, modeler, shape, tabs, stageName, rule, item, beDepends)
  } else if (type === 'MODEL' && model === 'MULTI_VALUE_CHOOSER') { // 多选
    rule.value = item.defaultValue ? JSON.parse(item.defaultValue) : []
    rule.props.multiple = true
    if (item.methodName) {
      rule.on = {
        focus: selectRuleFocusEvent(self, tabs, rule, stageName, item.methodName, item.parameters)
      }
    }
    return ruleSetMultiValueAndEvent(modeler, shape, tabs, rule, beDepends)
  } else if (type === 'MODEL' && model === 'MULTI_VALUE_INPUT') { // 多值输入
    rule.value = item.defaultValue ? JSON.parse(item.defaultValue) : []
    rule.props = {
      disabled: item.disabled,
      multiple: true,
      filterable: true, // 是否可搜索
      filterMethod: function(val) {
        rule.options = [{ value: val, label: val }]
      }
    }
    return ruleSetMultiValueAndEvent(modeler, shape, tabs, rule, beDepends)
  }
}

/**
 * 字符串转大写
 *
 * @param {String}} str
 */
function toUpperCase(str) {
  return str ? str.toUpperCase() : null
}

export default { getShapePanel, emptyShapePanels }
